package org.qdog.pk.pkscrape.test;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.object.StoredProcedure;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @since 08/2011
 */
public class DatabaseInitializer
{
  private static final Logger log = Logger.getLogger( DatabaseInitializer.class );
  private Resource[]          setupScripts = new Resource[0];
  private Resource[]          tearDownScripts = new Resource[0];
  private DataSource          dataSource;

  @PostConstruct
  public void setup() throws Exception {
    for (Resource createScript : setupScripts) {
      try {
        executeScript(createScript);
      } catch (Exception e) {
        log.debug("Could not execute create script [" + createScript + "]", e);
      }
    }
  }

	@PreDestroy
	public void cleanUp() throws Exception {
    log.info("Spring Container is destroy! Customer clean up");
    Assert.notNull(dataSource);
    log.info("Executing tearDown scripts: " + Arrays.asList(tearDownScripts));
    for (Resource dropScript : tearDownScripts) {
      try {
        executeScript(dropScript);
      } catch (Exception e) {
        log.debug("Could not execute drop script [" + dropScript + "]", e);
      }
    }
  }

  /**
   * @param script Resource to execute
   */
  @SuppressWarnings( "unchecked" )
  private void executeScript( final Resource script )
  {
    TransactionTemplate transactionTemplate = new TransactionTemplate( new DataSourceTransactionManager( dataSource ) );
    transactionTemplate.execute( new TransactionCallback() {

      public Object doInTransaction( final TransactionStatus status )
      {
        JdbcTemplate jdbcTemplate = new JdbcTemplate( dataSource );
        String[] scripts;
        try
        {
          scripts = StringUtils.delimitedListToStringArray( stripComments( IOUtils.readLines( script
              .getInputStream() ) ), ";" );
        }
        catch ( IOException e )
        {
          throw new BeanInitializationException( "Cannot load script from [" + script + "]", e );
        }
        for ( String script2 : scripts )
        {
          final String script = script2.trim();
          if ( StringUtils.hasText( script ) )
          {
            try
            {
              if (StringUtils.startsWithIgnoreCase(script2, "exec ")) {
                // assume stored procedure call -- currently doesn't support parameters. add as needed.
                script2 = script2.substring(5);
                SimpleStoredProcedure sp = new SimpleStoredProcedure(dataSource, script2);
                sp.execute();
              }  else {
                jdbcTemplate.execute( script2 );
              }
            } catch ( DataAccessException e ) {
              log.warn( "Script failure: " + e.getMessage() );
              if ( !script.toUpperCase().startsWith( "DROP" )) {
                throw e;
              }
            }
          }
        }
        return null;
      }

    } );

  }

  private class SimpleStoredProcedure extends StoredProcedure {
      SimpleStoredProcedure (DataSource ds, String name) {
          super(ds,name);
      }
  }

  private String stripComments( final List<String> list )
  {
    StringBuffer buffer = new StringBuffer();
    for ( String line : list )
    {
      if ( !line.startsWith( "//" ) && !line.startsWith( "--" ) )
      {
        buffer.append( line + "\n" );
      }
    }
    return buffer.toString();
  }

  public Resource[] getSetupScripts() {
    return setupScripts;
  }

  public void setSetupScripts(Resource[] setupScripts) {
    this.setupScripts = setupScripts;
  }

  public Resource[] getTearDownScripts() {
    return tearDownScripts;
  }

  public void setTearDownScripts(Resource[] tearDownScripts) {
    this.tearDownScripts = tearDownScripts;
  }

  public DataSource getDataSource()
  {
    return dataSource;
  }

  public void setDataSource( final DataSource dataSource )
  {
    this.dataSource = dataSource;
  }

}
